/*** xrkmonitor license ***

   Copyright (c) 2020 by rockdeng

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


   字符云监控(xrkmonitor) 开源版 (c) 2019 by rockdeng
   当前版本：v1.0
   使用授权协议： apache license 2.0

   开源版主页：http://open.xrkmonitor.com
   云版本主页：http://xrkmonitor.com
  

   云版本为开源版提供永久免费告警通道支持，告警通道支持短信、邮件、
   微信等多种方式，欢迎使用

   javascript 监控插件 monitor_website:
        监控并上报网站用户的基本信息包括浏览器类型、操作系统类型、地理位置等，
    并可上报网站 js 错误信息、img 加载失败信息，方便对不同浏览器做兼容。使用
    方法：下载部署配置文件, 将该文件以及配置文件放入 js api 所在目录，网站入
    口出引入 js api 文件即可，详情请查看在线文档.

****/



function xrk_monitor_monitor_website() {
    var _plugin_monitor_website = {
        plugin_name : 'monitor_website',
        xrk_api:null,
        os_info: '',
        explorer_info: '',
        check_timer_id:0,
        check_imgs: new Array(),
        win_href:'',
        last_show_try_info_time:0,
        xrk_window_id:0,
        img_load_timeout:10000,
        show_info_per_time:25000,
        win_obj:null,
        obj: null,
        b_exception:false,
    
        info_log: function(msg, pos) { 
            if(typeof pos != 'undefined')
                this._add_log('info', pos + msg);
            else
                this._add_log('info', '['+this.plugin_name+':log:0]' + msg);
        },
    
        error_log: function(msg, pos) { 
            if(typeof pos != 'undefined')
                this._add_log('error', pos + msg);
            else
                this._add_log('error', '['+this.plugin_name+':log:0]' + msg);
        },
        _add_log: function(log_type, msg) {
            var p_self = this;
            if(p_self.obj.config.logconfig_id == 0) {
                p_self.xrk_api.local_msg('plugin:' + p_self.plugin_name+ ' log config id not set !');
                return;
            }
            if(p_self.obj.config.logconfig_type.indexOf(log_type) == -1)
            {
                p_self.xrk_api.debug ? p_self.xrk_api.local_msg('skip log:('+log_type+') - '+msg) : '';
                return;
            }
            p_self.xrk_api.debug ? p_self.xrk_api.local_msg('report ' + log_type + ' log:' + msg) : ''; 
    
            var log = new Object();
            log.type = log_type;
            log.msg = msg;
            log.time = Date.parse(new Date());
            p_self.obj.logs.push(log);
            p_self.obj.cur_logs_strlen += msg.length;
            if(p_self.obj.cur_logs_strlen >= p_self.xrk_api.max_logs_strlen) {
                p_self.xrk_api.report_to_server();
            }
        }, 
    
        env_info: function() {
            return '[' + this.os_info + ', ' + this.explorer_info + ']';
        },
    
        imgLoadError: function (xrk_img, ctype) {
            if(typeof xrk_img != 'object')
                return;
            var msg = 'img error ' + this.env_info() + ', check:' + ctype;
            if(typeof(xrk_img.src) == 'string') {
                var isp =xrk_img.src.indexOf('/', 8);
                if(isp != -1)
                    msg += ', src:' + xrk_img.src.substring(isp);
                else
                    msg += ', src:' + xrk_img.src;
            }
            if(typeof(xrk_img.baseURI) == 'string') {
                var isp = xrk_img.baseURI.indexOf('/', 8);
                if(isp != -1)
                    msg += ', from:' + xrk_img.baseURI.substring(isp);
                else
                    msg += ', from:' + xrk_img.baseURI; 
            }
            if(typeof(xrk_img.outerHTML) == 'string')
                msg += ', html:' + xrk_img.outerHTML;
            this.error_log(msg);
            this.xrk_api.report_to_server();
        },
    
        checkImgStatus: function (doc, type) {
            var p_self = this;
            var imgs = null;
            if(p_self.b_exception)
                return;

            try {
                if(p_self.win_obj != window) {
                    p_self.xrk_api.local_msg('(checkImgStatus) window changed, remove it, window id:' + p_self.xrk_window_id);
                    p_self.b_exception = true;
                    return;
                }
                imgs = doc.getElementsByTagName('img');
            }catch(e) {
                if(p_self.check_timer_id != 0) {
                    clearTimeout(p_self.check_timer_id);
                    p_self.check_timer_id = 0;
                }
                p_self.xrk_api.local_msg('(checkImgStatus) get exception:' + e.message + ' href:' + p_self.win_href
                    + ', window:' + p_self.xrk_api.init_win_domain + ', timer:' +p_self.check_timer_id);
                p_self.b_exception = true;
                return;
            }
    
            var d_time = new Date().getTime();
            var i_wait_deal = 0;
            for(var i=0, j=0; i < imgs.length; i++) {
                for(j=0; j < p_self.check_imgs.length; j++) {
                    if(p_self.check_imgs[j].src === imgs[i].src)
                        break;
                }
                if(j < p_self.check_imgs.length && p_self.check_imgs[j].complete)
                    continue;

                if(j >= p_self.check_imgs.length) {
                    var c_img = new Object();
                    c_img.src = imgs[i].src;
                    c_img.add_time = d_time;
                    c_img.complete = false;
                    if(imgs[i].complete && imgs[i].naturalHeight == 0 && imgs[i].naturalWidth == 0) {
                        c_img.complete = true;
                        p_self.imgLoadError(imgs[i], 'size');
                    }
                    else if(!imgs[i].complete) {
                        if(imgs[i].addEventListener) 
                            imgs[i].addEventListener('error', function(){ 
                                c_img.complete = true; 
                                p_self.imgLoadError(this, 'error'); 
                            });
                        else if(imgs[i].attachEvent)
                            imgs[i].attachEvent('load', function(){ 
                                c_img.complete = true;
                                p_self.imgLoadError(this, 'error'); 
                            });
                        else
                            imgs[i].onerror = function() { p_self.imgLoadError(this, 'error'); }
                    }
                    p_self.check_imgs.push(c_img);
                }
                else if(p_self.check_imgs[j].add_time+p_self.img_load_timeout <= d_time){
                    if(imgs[i].naturalHeight == 0 && imgs[i].naturalWidth == 0)
                        p_self.imgLoadError(imgs[i], 'size');
                    p_self.check_imgs[j].complete = true;
                }
                if(!p_self.check_imgs[j].complete)
                    i_wait_deal++;
            }

            p_self.check_timer_id = setTimeout(function() { p_self.checkImgStatus(doc, type); }, 2000);
            if(p_self.xrk_api.debug)  {
                if(d_time >= p_self.last_show_try_info_time+p_self.show_info_per_time) {
                    p_self.last_show_try_info_time = d_time;
                    p_self.xrk_api.local_msg("run check img, window:" + p_self.xrk_window_id 
                        + ', total count:' + imgs.length + ', wait:' + i_wait_deal
                        + ', timer:' + p_self.check_timer_id + ', domain:' 
                        + p_self.xrk_api.init_win_domain + ', href:' + p_self.win_href);
                }
            }
        },
    
        content_loaded: function (win, fn, xrk) {
            var done = false, _top = true,
            doc = win.document,
            root = doc.documentElement,
            modern = doc.addEventListener,
    
            add = modern ? 'addEventListener' : 'attachEvent',
            rem = modern ? 'removeEventListener' : 'detachEvent',
            pre = modern ? '' : 'on',
    
            init = function(e) {
                if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
                (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
                if (!done && (done = true)) fn.call(xrk, doc, e.type || e);
            },
    
            poll = function() {
                try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
                init('poll');
            };
    
            if (doc.readyState == 'complete') fn.call(xrk, doc, 'lazy');
            else {
                if (!modern && root.doScroll) {
                    try { _top = !win.frameElement; } catch(e) { }
                    if (_top) poll();
                }
                doc[add](pre + 'DOMContentLoaded', init, false);
                doc[add](pre + 'readystatechange', init, false);
                win[add](pre + 'load', init, false);
            }
        },
    
        detectExplorer: function (win) {
            var explorer = win.navigator.userAgent.toLowerCase();
            // ie
            if (explorer.indexOf("msie") >= 0) {
                var vers=explorer.match(/msie ([\d.]+)/);
                if(vers.length >= 2)
                    return {type:"IE", version:vers[1]};
                return {type:"IE", version:'Unknow.Unknow'};
            }
            // >= ie11 
            else if (explorer.indexOf('trident/') >= 0) {
                var vers = explorer.indexOf('rv:');
                if(vers != -1) {
                    var vend = explorer.indexOf(')', vers);
                    if(vend == -1) 
                        vend = explorer.indexOf('.', vers);
                    if(vend != -1) {
                        var vstr = explorer.substring(vers+3, vend);
                        return {type:"IE", version: vstr };
                    }
                }
                return {type:"IE", version: 'Unknow.Unknow'};
            }
            // microsoft edge
            else if(explorer.indexOf('edge/') >= 0) {
                var vers=explorer.match(/edge\/([\d.]+)/);  
                if(vers.length >= 2)
                    return {type:'Edge', version:vers[1]};
                return {type:"Edge", version:'Unknow.Unknow'};
            }
            //firefox
            else if (explorer.indexOf("firefox") >= 0) {
                var vers=explorer.match(/firefox\/([\d.]+)/);
                if(vers.length >= 2)
                    return {type:"Firefox", version:vers[1]};
                return {type:"Firefox", version:'Unknow.Unknow'};
            }
            //Chrome
            else if(explorer.indexOf("chrome") >= 0){
                var vers=explorer.match(/chrome\/([\d.]+)/);
                if(vers.length >= 2)
                    return {type:"Chrome", version:vers[1]};
                return {type:"Chrome", version:'Unknow.Unknow'};
            }
            //Opera
            else if(explorer.indexOf("opera") >= 0){
                var vers=explorer.match(/opera.([\d.]+)/);
                if(vers.length >= 2)
                    return {type:"Opera", version:vers[1]};
                return {type:"Opera", version:'Unknow.Unknow'};
            }
            //Safari
            else if(explorer.indexOf("safari") >= 0){
                var vers=explorer.match(/safari\/([\d.]+)/);
                if(vers.length >= 2)
                    return {type:"Safari", version:vers[1]};
                return {type:"Safari", version:'Unknow.Unknow'};
            }
            this.xrk_api.local_msg('unknow explorer:' + explorer);
            return {type:"Unknow", version:'Unknow.Unknow'};
        },
    
        detectOS: function (win) { 
            if(typeof(win.navigator.userAgent) != 'string')
                return { type:'Unknow', os:'Unknow' };
            var p_self = this;
            var userAgent = win.navigator.userAgent.toLowerCase();
            var xrk = p_self.xrk_api;
            var xrk_config = p_self.obj.config;
            if(userAgent.indexOf("android") != -1) 
                return { type:'mobile', os:'Android' };
            if(userAgent.indexOf("iphone") != -1) 
                return { type:'mobile', os:'iPhone' };
            if(userAgent.indexOf("symbianos") != -1) 
                return { type:'mobile', os:'SymbianOS' };
            if(userAgent.indexOf("windows phone") != -1) 
                return { type:'mobile', os:'Windows Phone' };
            if(userAgent.indexOf("iphone") != -1) 
                return { type:'mobile', os:'iPhone' };
            if(userAgent.indexOf("ipad") != -1) 
                return { type:'mobile', os:'iPad' };
            if(userAgent.indexOf("ipod") != -1) 
                return { type:'mobile', os:'iPod' };
            if(userAgent.indexOf("blackberry") != -1) 
                return { type:'mobile', os:'BlackBerry' };
            try {
                if(typeof(document.body.ontouchend) === 'function' || userAgent.indexOf('mobile') != -1) {
                    xrk.local_msg('unknow mobile os:' + userAgent);
                    return { type:'mobile', os:'Unknow' };
                }
            }catch(e) {
                if(userAgent.indexOf('mobile') != -1) {
                    xrk.local_msg('unknow mobile os:' + userAgent);
                    return { type:'mobile', os:'Unknow' };
                }
            }
        
            if(userAgent.indexOf("windows nt 10.0")!= -1) 
                return { type:'pc', os:'Windows 10' };
            if(userAgent.indexOf("windows nt 6.2")!= -1) 
                return { type:'pc', os:'Windows 8' };
            if(userAgent.indexOf("windows nt 6.1")!= -1) 
                return { type:'pc', os:'Windows 7' };
            if(userAgent.indexOf("windows nt 6.0")!= -1) 
                return { type:'pc', os:'Windows Vista' };
            if(userAgent.indexOf("windows nt 5.1")!= -1) 
                return { type:'pc', os:'Windows XP' };
            if(userAgent.indexOf("windows nt 5.0")!= -1) 
                return { type:'pc', os:'Windows 2000' };
            if(userAgent.indexOf("mac")!= -1) 
                return { type:'pc', os:'Mac/iOS' };
            if(userAgent.indexOf("x11")!= -1)
                return { type:'pc', os:'UNIX' };
            if(userAgent.indexOf("linux")!= -1) 
                return { type:'pc', os:'Linux' };
            xrk.local_msg('unknow pc os:' + userAgent);
            return { type:'pc', os:'Unknow' };
        },
    
        report_baseinfo: function (win) {
            var p_self = this;
            var xrk = p_self.xrk_api;
            var xrk_config = p_self.obj.config;
            if(typeof(win.navigator.userAgent) != 'string') {
                var msg = 'unknow navigator.userAgent ';
                if(typeof win.navigator.appVersion == 'string')
                    msg += ', info:' + win.navigator.appVersion;
                p_self.info_log(msg);
            }
            else {
                if(typeof(win.navigator.userAgent) != 'undefined')
                    xrk.local_msg('useragent:' + win.navigator.userAgent);
    
                var osinfo = p_self.detectOS(win);
                p_self.os_info = osinfo.os; 
                if(osinfo.type == 'mobile') 
                    xrk.report_strattr_add(xrk_config.strattr_id_mobile_os, osinfo.os, 1);
                else
                    xrk.report_strattr_add(xrk_config.strattr_id_pc_os, osinfo.os, 1);

                var einfo = p_self.detectExplorer(win);
                var vers = einfo.version.split('.');
                var verinfo = vers[0];
                if(vers.length >= 2)
                    verinfo = vers[0] + '.' + vers[1];
                p_self.explorer_info = einfo.type+' '+verinfo;
                xrk.report_strattr_add(xrk_config.strattr_id_explorer, einfo.type, 1);
                xrk.report_strattr_add(xrk_config.strattr_id_explorer_ver, einfo.type+' '+verinfo, 1);
                xrk.set_attrs_plugin(xrk_config.plugin_id);
                if(osinfo.os.indexOf('Unknow') != -1 || einfo.version.indexOf('Unknow') != -1)
                    p_self.info_log('unknow baseinfo, ua:'+win.navigator.userAgent);
            }
        
            xrk.reg_on_response(function() {
                if(xrk.local_out_ip != '')
                    xrk.report_strattr_add(xrk_config.strattr_id_ip_geo, xrk.local_out_ip, 1);
                if(xrk.max_api_delay > 0)
                    xrk.report_attr_set(xrk_config.attr_id_net_delay, xrk.max_api_delay);
                if(xrk.max_api_delay < 20)
                    xrk.report_strattr_add(xrk_config.strattr_id_net_delay, '上报耗时小于 20 ms', 1);
                else if(xrk.max_api_delay >= 20 && xrk.max_api_delay < 50)
                    xrk.report_strattr_add(xrk_config.strattr_id_net_delay, '上报耗时在 [20-50) ms', 1);
                else if(xrk.max_api_delay >= 50 && xrk.max_api_delay < 100)
                    xrk.report_strattr_add(xrk_config.strattr_id_net_delay, '上报耗时在 [50-100) ms', 1);
                else if(xrk.max_api_delay >= 100 && xrk.max_api_delay < 200)
                    xrk.report_strattr_add(xrk_config.strattr_id_net_delay, '上报耗时在 [100-200) ms', 1);
                else if(xrk.max_api_delay >= 200 && xrk.max_api_delay < 500)
                    xrk.report_strattr_add(xrk_config.strattr_id_net_delay, '上报耗时在 [200-500) ms', 1);
                else if(xrk.max_api_delay >= 500 && xrk.max_api_delay < 1000)
                    xrk.report_strattr_add(xrk_config.strattr_id_net_delay, '上报耗时在 [500-1000) ms', 1);
                else 
                    xrk.report_strattr_add(xrk_config.strattr_id_net_delay, '上报耗时大于等于 1秒', 1);
                xrk.set_attrs_plugin(xrk_config.plugin_id, xrk_config.plugin_ver);
                xrk.reg_on_response(null);
                xrk.report_to_server();
            });
            xrk.report_to_server();
        },
    
        monitor_window: function(obj_plugin, win) {
            var p_self  = this;
            p_self.xrk_api = win.self_xrk;
            p_self.obj = obj_plugin;
            p_self.config = obj_plugin.config;
            p_self.win_href = win.location.href;
            p_self.win_obj = win;
            if(p_self.xrk_api.load_more == 0) {
                p_self.report_baseinfo(win);
                if(p_self.xrk_api.debug) {
                    p_self.xrk_api.local_msg('on plugin: ' + p_self.plugin_name + ' load(first)' + ', href:' + p_self.win_href);
                }
            }
            else {
                var osinfo = p_self.detectOS(win);
                p_self.os_info = osinfo.os;
                var einfo = p_self.detectExplorer(win);
                var vers = einfo.version.split('.');
                var verinfo = vers[0];
                if(vers.length >= 2)
                    verinfo = vers[0] + '.' + vers[1];
                p_self.explorer_info = einfo.type+' '+verinfo;
                if(p_self.xrk_api.debug) {
                    p_self.xrk_api.local_msg('on plugin: ' + p_self.plugin_name + ' load (more)' + ', href:' + p_self.win_href);
                }
            }
            p_self._monitor_window_inner(win);
        },
    
        getJsErrMsg : function(errMsg) {
            var js_file = '';
            if(errMsg.filename) {
                var isp = errMsg.filename.lastIndexOf('/');
                if(isp != -1)
                    js_file = errMsg.filename.substring(isp+1);
                else
                    js_file = errMsg.filename;
            }
            if(this.xrk_api.is_xrkmonitor_js(js_file)) {
                if(this.xrk_api.debug) {
                    var msg = 'xrkmonitor js error, msg:' + (errMsg.message ? errMsg.message : '');
                    msg += ', file:' + js_file;
                    msg += ', lineno:' + (errMsg.lineno ? errMsg.lineno : '');
                    msg += ', colno:' + (errMsg.colno ? errMsg.colno : '');
                    this.xrk_api.local_msg(msg);
                }
            }
            else
            {
                var msg = 'js error ' + this.env_info();
                msg += ' msg:' + (errMsg.message ? errMsg.message : '');
                msg += ', file:' + js_file;
                msg += ', lineno:' + (errMsg.lineno ? errMsg.lineno : '');
                msg += ', colno:' + (errMsg.colno ? errMsg.colno : '');
                this.error_log(msg);
                this.xrk_api.report_to_server();
            }
            return true;
        },
    
        getUnloadMsg: function(evmsg) {
            var p_self = this;
            try {
                if(this.xrk_api.debug) {
                    var msg = 'on unload:';
                    if(typeof(evmsg.target.documentURI) == 'string')
                        msg += evmsg.target.documentURI;
                    else if(typeof(evmsg.target.document.URL) == 'string')
                        msg += evmsg.target.document.URL;
                    else if(typeof(evmsg.target.baseURI) == 'string')
                        msg += evmsg.target.baseURI;
                    msg += ', window id:' + p_self.xrk_window_id;
                    this.xrk_api.local_msg(msg);
                }
            }catch(e) { 
                p_self.xrk_api.local_msg('(getUnloadMsg) get exception:' + e.message 
                    + ', window id:' + p_self.xrk_window_id);
            }
            p_self.b_exception = true;
            if(p_self.check_timer_id != 0) {
                clearTimeout(p_self.check_timer_id);
                p_self.check_timer_id = 0;
            }
        },
    
        _monitor_window_inner: function(win)  {
            var p_self = this;
            p_self.xrk_window_id = new Date().getTime();
          try {
            if(win.addEventListener)  {
                win.addEventListener('error', function(evmsg){ p_self.getJsErrMsg(evmsg); });
                win.addEventListener('beforeunload', function(evmsg){ p_self.getUnloadMsg(evmsg); });
                win.addEventListener('unload', function(evmsg){ p_self.getUnloadMsg(evmsg); });
            }
            else if(win.attachEvent) {
                win.attachEvent('onerror', function(evmsg){ p_self.getJsErrMsg(evmsg); });
                win.attachEvent('onbeforeunload', function(evmsg){ p_self.getUnloadMsg(evmsg); });
                win.attachEvent('onunload', function(evmsg){ p_self.getUnloadMsg(evmsg); });
            }
            else  {
                win.onerror = function(errorMessage, scriptURI, lineNumber, columnNumber, errorObj) {
                    var errObj = new Object();
                    errObj.filename = scriptURI;
                    errObj.message = errorMessage;
                    errObj.lineno = lineNumber;
                    errObj.colno = columnNumber;
                    p_self.getJsErrMsg(errObj);
                };
                if(typeof(win.onbeforeunload) != 'function')
                    win.onbeforeunload = function() { p_self.getUnloadMsg(); };
            }
            this.content_loaded(win, p_self.checkImgStatus, p_self);
          }catch(e) {
                p_self.error_log('(_monitor_window_inner) get exception:' + e.message);
            }
        }
    };
    return _plugin_monitor_website;
}

